﻿<!DOCTYPE html>
<html>
<head>
<title>webpack配置</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */

/* RESET
=============================================================================*/

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
}

/* BODY
=============================================================================*/

body {
  font-family: Helvetica, arial, freesans, clean, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  color: #333;
  background-color: #fff;
  padding: 20px;
  max-width: 960px;
  margin: 0 auto;
}

body>*:first-child {
  margin-top: 0 !important;
}

body>*:last-child {
  margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
  margin: 16px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
  font-size: inherit;
}

h1 {
  font-size: 28px;
  text-align: center;
}

h2 {
  font-size: 26px;
}

h3 {
  font-size: 22px;
}

h4 {
  font-size: 18px;
}

h5 {
  font-size: 16px;
}

h6 {
  font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
  margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
  color: #4183C4;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
  padding-left: 30px;
}

ul li > :first-child, 
ol li > :first-child, 
ul li ul:first-of-type, 
ol li ol:first-of-type, 
ul li ol:first-of-type, 
ol li ul:first-of-type {
  margin-top: 0px;
  margin-bottom: 0px;
}

ul ul, ul ol, ol ol, ol ul {
  margin-bottom: 0;
}

dl {
  padding: 0;
}

dl dt {
  font-size: 14px;
  font-weight: bold;
  font-style: italic;
  padding: 0;
  margin: 15px 0 5px;
}

dl dt:first-child {
  padding: 0;
}

dl dt>:first-child {
  margin-top: 0px;
}

dl dt>:last-child {
  margin-bottom: 0px;
}

dl dd {
  margin: 0 0 15px;
  padding: 0 15px;
}

dl dd>:first-child {
  margin-top: 0px;
}

dl dd>:last-child {
  margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
  font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
  margin: 0 0px;
  padding: 0px 3px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #eee;
  color: #00d;
  border-radius: 3px;
}

pre>code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent;
}

pre {
  background-color: #f8f8f8;
  border: 1px solid #ccc;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre code, pre tt {
  background-color: transparent;
  border: none;
}

kbd {
    -moz-border-bottom-colors: none;
    -moz-border-left-colors: none;
    -moz-border-right-colors: none;
    -moz-border-top-colors: none;
    background-color: #DDDDDD;
    background-image: linear-gradient(#F1F1F1, #DDDDDD);
    background-repeat: repeat-x;
    border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
    border-image: none;
    border-radius: 2px 2px 2px 2px;
    border-style: solid;
    border-width: 1px;
    font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
    line-height: 16px;
    padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
  border-left: 4px solid #42b983;
  padding: 0 15px;
  color: #777;
  background: #f8f8f8;
}

blockquote>:first-child {
  margin-top: 0px;
}

blockquote>:last-child {
  margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
  clear: both;
  margin: 15px 0;
  height: 0px;
  overflow: hidden;
  border: none;
  background: transparent;
  border-bottom: 4px solid #ddd;
  padding: 0;
}

/* TABLES
=============================================================================*/
table {
	margin: 0 auto;
	border-collapse: collapse;
	width: 100%;
}

table th, table td {
  border: 1px solid #ccc;
  padding: 6px 13px; 
}

table th {
  font-weight: bold;
  text-align: center !important;
  background-color: #9ec68e;
}

table tr {
  border-top: 1px solid #ccc;
  background-color: #fff;
}

table tr:nth-child(2n) {
  background-color: #dff0d8;
}

/* IMAGES
=============================================================================*/

img {
  max-width: 100%;
}

p > img {
    display: table;
    margin: 0 auto;
}
</style>
<style type="text/css">
.highlight  { background: #ffffff; }
.highlight .c { color: #999988; font-style: italic } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { font-weight: bold } /* Keyword */
.highlight .o { font-weight: bold } /* Operator */
.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #999999 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #aaaaaa } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { font-weight: bold } /* Keyword.Constant */
.highlight .kd { font-weight: bold } /* Keyword.Declaration */
.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #009999 } /* Literal.Number */
.highlight .s { color: #d14 } /* Literal.String */
.highlight .na { color: #008080 } /* Name.Attribute */
.highlight .nb { color: #0086B3 } /* Name.Builtin */
.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
.highlight .no { color: #008080 } /* Name.Constant */
.highlight .ni { color: #800080 } /* Name.Entity */
.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
.highlight .nn { color: #555555 } /* Name.Namespace */
.highlight .nt { color: #000080 } /* Name.Tag */
.highlight .nv { color: #008080 } /* Name.Variable */
.highlight .ow { font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #009999 } /* Literal.Number.Float */
.highlight .mh { color: #009999 } /* Literal.Number.Hex */
.highlight .mi { color: #009999 } /* Literal.Number.Integer */
.highlight .mo { color: #009999 } /* Literal.Number.Oct */
.highlight .sb { color: #d14 } /* Literal.String.Backtick */
.highlight .sc { color: #d14 } /* Literal.String.Char */
.highlight .sd { color: #d14 } /* Literal.String.Doc */
.highlight .s2 { color: #d14 } /* Literal.String.Double */
.highlight .se { color: #d14 } /* Literal.String.Escape */
.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
.highlight .si { color: #d14 } /* Literal.String.Interpol */
.highlight .sx { color: #d14 } /* Literal.String.Other */
.highlight .sr { color: #009926 } /* Literal.String.Regex */
.highlight .s1 { color: #d14 } /* Literal.String.Single */
.highlight .ss { color: #990073 } /* Literal.String.Symbol */
.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #008080 } /* Name.Variable.Class */
.highlight .vg { color: #008080 } /* Name.Variable.Global */
.highlight .vi { color: #008080 } /* Name.Variable.Instance */
.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
.pl-c {
    color: #969896;
}

.pl-c1,.pl-mdh,.pl-mm,.pl-mp,.pl-mr,.pl-s1 .pl-v,.pl-s3,.pl-sc,.pl-sv {
    color: #0086b3;
}

.pl-e,.pl-en {
    color: #795da3;
}

.pl-s1 .pl-s2,.pl-smi,.pl-smp,.pl-stj,.pl-vo,.pl-vpf {
    color: #333;
}

.pl-ent {
    color: #63a35c;
}

.pl-k,.pl-s,.pl-st {
    color: #a71d5d;
}

.pl-pds,.pl-s1,.pl-s1 .pl-pse .pl-s2,.pl-sr,.pl-sr .pl-cce,.pl-sr .pl-sra,.pl-sr .pl-sre,.pl-src,.pl-v {
    color: #df5000;
}

.pl-id {
    color: #b52a1d;
}

.pl-ii {
    background-color: #b52a1d;
    color: #f8f8f8;
}

.pl-sr .pl-cce {
    color: #63a35c;
    font-weight: bold;
}

.pl-ml {
    color: #693a17;
}

.pl-mh,.pl-mh .pl-en,.pl-ms {
    color: #1d3e81;
    font-weight: bold;
}

.pl-mq {
    color: #008080;
}

.pl-mi {
    color: #333;
    font-style: italic;
}

.pl-mb {
    color: #333;
    font-weight: bold;
}

.pl-md,.pl-mdhf {
    background-color: #ffecec;
    color: #bd2c00;
}

.pl-mdht,.pl-mi1 {
    background-color: #eaffea;
    color: #55a532;
}

.pl-mdr {
    color: #795da3;
    font-weight: bold;
}

.pl-mo {
    color: #1d3e81;
}
.task-list {
padding-left:10px;
margin-bottom:0;
}

.task-list li {
    margin-left: 20px;
}

.task-list-item {
list-style-type:none;
padding-left:10px;
}

.task-list-item label {
font-weight:400;
}

.task-list-item.enabled label {
cursor:pointer;
}

.task-list-item+.task-list-item {
margin-top:3px;
}

.task-list-item-checkbox {
display:inline-block;
margin-left:-20px;
margin-right:3px;
vertical-align:1px;
}
</style>
</head>
<body>
<h1 id="webpack-">webpack配置</h1>
<p>配置 Webpack 的方式有两种：</p>
<ol>
<li>通过一个 JavaScript 文件描述配置，例如使用 <code>webpack.config.js</code> 文件里的配置；</li><li>执行 Webpack 可执行文件时通过命令行参数传入，例如 <code>webpack --devtool source-map</code>。</li></ol>
<p>这两种方式可以相互搭配，例如执行 Webpack 时通过命令 <code>webpack --config webpack-dev.config.js</code> 指定配置文件，再去 <code>webpack-dev.config.js</code> 文件里描述部分配置。</p>
<p>按照配置<strong>所影响的功能</strong>来划分，可分为：</p>
<ul>
<li><strong>Entry</strong> 配置模块的入口；</li><li><strong>Output</strong> 配置如何输出最终想要的代码；</li><li><strong>Module</strong> 配置处理模块的规则；</li><li><strong>Resolve</strong> 配置寻找模块的规则；</li><li><strong>Plugins</strong> 配置扩展插件；</li><li><strong>DevServer</strong> 配置 DevServer；</li><li><strong>其它配置项</strong> 其它零散的配置项；</li><li><strong>整体配置结构</strong> 整体地描述各配置项的结构；</li><li><strong>多种配置类型</strong> 配置文件不止可以返回一个 Object，还有其他返回形式；</li><li><strong>配置总结</strong> 寻找配置 Webpack 的规律，减少思维负担。</li></ul>
<h2 id="entry">Entry</h2>
<p>Webpack 在寻找相对路径的文件时会以 context 为根目录，context 默认为执行启动 Webpack 时所在的当前工作目录。</p>
<p>如果想改变 context 的默认配置，可以在配置文件里设置：</p>
<pre><code>module.exports = {
  context: path.resolve(__dirname, &#39;app&#39;)
}
</code></pre><p>注意， context 必须是一个绝对路径的字符串。 除此之外，还可以通过在启动 Webpack 时带上参数 <code>webpack --context</code> 来设置 context。    </p>
<h3 id="chunk-">Chunk 名称</h3>
<p>Webpack 会为每个生成的 Chunk 取一个名称，Chunk 的名称和 Entry 的配置有关：</p>
<ul>
<li>如果 entry 是一个 <code>string</code> 或 <code>array</code>，就只会生成一个 Chunk，这时 Chunk 的名称是 <code>main</code>；</li><li>如果 entry 是一个 <code>object</code>，就可能会出现多个 Chunk，这时 Chunk 的名称是 <code>object</code> 键值对里键的名称。</li></ul>
<h3 id="-entry">配置动态 Entry</h3>
<p>假如项目里有多个页面需要为每个页面的入口配置一个 Entry ，但这些页面的数量可能会不断增长，则这时 Entry 的配置会受到到其他因素的影响导致不能写成静态的值。其解决方法是把 Entry 设置成一个函数去动态返回上面所说的配置，代码如下：</p>
<pre><code>// 同步函数
entry: () =&gt; {
  return {
    a:&#39;./pages/a&#39;,
    b:&#39;./pages/b&#39;,
  }
};
// 异步函数
entry: () =&gt; {
  return new Promise((resolve)=&gt;{
    resolve({
       a:&#39;./pages/a&#39;,
       b:&#39;./pages/b&#39;,
    });
  });
};
</code></pre><h2 id="output">Output</h2>
<p><code>output</code> 配置如何输出最终想要的代码。<code>output</code> 是一个 <code>object</code>，里面包含一系列配置项：</p>
<h3 id="filename">filename</h3>
<p><code>output.filename</code> 配置输出文件的名称，为 string 类型。 如果只有一个输出文件，则可以把它写成静态不变的：</p>
<pre><code>filename: &#39;bundle.js&#39;
</code></pre><p>但是在有多个 Chunk 要输出时，就需要借助模版和变量了。前面说到 Webpack 会为每个 Chunk取一个名称，可以根据 Chunk 的名称来区分输出的文件名：</p>
<pre><code>filename: &#39;[name].js&#39;
</code></pre><p>代码里的 <code>[name]</code> 代表用内置的 <code>name</code> 变量去替换<code>[name]</code>，这时你可以把它看作一个字符串模块函数， 每个要输出的 Chunk 都会通过这个函数去拼接出输出的文件名称。  </p>
<table>
<thead>
<tr>
<th>变量名</th>
<th style="text-align:left">含义           </th>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td style="text-align:left">Chunk 的唯一标识，从0开始</td>
</tr>
<tr>
<td>name</td>
<td style="text-align:left">Chunk 的名称      </td>
</tr>
<tr>
<td>hash</td>
<td style="text-align:left">Chunk 的唯一标识的 Hash 值</td>
</tr>
<tr>
<td>chunkhash</td>
<td style="text-align:left">Chunk 内容的 Hash 值</td>
</tr>
</tbody>
</table>
<p>其中 <code>hash</code> 和 <code>chunkhash</code> 的长度是可指定的，<code>[hash:8]</code> 代表取8位 Hash 值，默认是20位。</p>
<blockquote>
<p>注意 ExtractTextWebpackPlugin 插件是使用 <code>contenthash</code> 来代表哈希值而不是 <code>chunkhash</code>， 原因在于 ExtractTextWebpackPlugin 提取出来的内容是代码内容本身而不是由一组模块组成的 Chunk。</p>
</blockquote>
<h3 id="chunkfilename">chunkFilename</h3>
<p><code>output.chunkFilename</code> 配置无入口的 Chunk 在输出时的文件名称。 <code>chunkFilename</code> 和上面的 <code>filename</code> 非常类似，但 chunkFilename 只用于指定在运行过程中生成的 Chunk 在输出时的文件名称。 常见的会在运行时生成 Chunk 场景有在使用 <code>CommonChunkPlugin</code>、使用 <code>import(&#39;path/to/module&#39;)</code> 动态加载等时。 chunkFilename 支持和 filename 一致的内置变量。</p>
<p><strong>path</strong></p>
<p><code>output.path</code> 配置输出文件存放在本地的目录，必须是 string 类型的绝对路径。通常通过 Node.js 的 <code>path</code> 模块去获取绝对路径：</p>
<pre><code>path: path.resolve(__dirname, &#39;dist_[hash]&#39;)
</code></pre><h3 id="publicpath">publicPath</h3>
<p>在复杂的项目里可能会有一些构建出的资源需要异步加载，加载这些异步资源需要对应的 URL 地址。</p>
<p><code>output.publicPath</code> 配置发布到线上资源的 URL 前缀，为string 类型。 默认值是空字符串 <code>&#39;&#39;</code>，即使用相对路径。</p>
<p>把构建出的资源文件上传到 CDN 服务上，以利于加快页面的打开速度。配置代码如下：</p>
<pre><code>filename:&#39;[name]_[chunkhash:8].js&#39;
publicPath: &#39;https://cdn.example.com/assets/&#39;
</code></pre><p>这时发布到线上的 HTML 在引入 JavaScript 文件时就需要：</p>
<pre><code>&lt;script src=&#39;https://cdn.example.com/assets/a_12345678.js&#39;&gt;&lt;/script&gt;
</code></pre><p>使用该配置项时要小心，稍有不慎将导致资源加载404错误。</p>
<p><code>output.path</code> 和 <code>output.publicPath</code> 都支持字符串模版，内置变量只有一个：<code>hash</code> 代表一次编译操作的 Hash 值。</p>
<h3 id="crossoriginloading">crossOriginLoading</h3>
<p>Webpack 输出的部分代码块可能需要异步加载，而异步加载是通过 <code>JSONP</code> 方式实现的。 <code>JSONP</code> 的原理是动态地向 HTML 中插入一个 <code>&lt;script src=&quot;url&quot;&gt;&lt;/script&gt;</code> 标签去加载异步资源。</p>
<p><code>output.crossOriginLoading</code> 则是用于配置这个异步插入的标签的 <code>crossorigin</code> 值。</p>
<p>script 标签的 <code>crossorigin</code> 属性可以取以下值：</p>
<ul>
<li><code>false</code>(默认) 在加载此脚本资源时不会带上用户的 Cookies；</li><li><code>use-credentials</code> 在加载此脚本资源时会带上用户的 Cookies。</li></ul>
<p>通常用设置 <code>crossorigin</code> 来获取异步加载的脚本执行时的详细错误信息。</p>
<h3 id="librarytarget-library">libraryTarget 和 library</h3>
<p>当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到它们。</p>
<ul>
<li><code>output.libraryTarget</code> 配置以何种方式导出库。</li><li><code>output.library</code> 配置导出库的名称。</li></ul>
<p>假如配置了 <code>output.library=&#39;LibraryName&#39;</code>，则输出和使用的代码如下：</p>
<pre><code>// Webpack 输出的代码
var LibraryName = lib_code;

// 使用库的方法
LibraryName.doSomething();
</code></pre><p>假如 <code>output.library</code> 为空，则将直接输出：<code>lib_code</code></p>
<blockquote>
<p>其中 <code>lib_code</code> 代指导出库的代码内容，是有返回值的一个自执行函数。</p>
</blockquote>
<p>它们通常搭配在一起使用。</p>
<p><code>output.libraryTarget</code> 是字符串的枚举类型，支持以下配置。</p>
<h3 id="var-">var (默认)</h3>
<p>编写的库将通过 <code>var</code> 被赋值给通过 <code>library</code> 指定名称的变量。</p>
<h3 id="commonjs">commonjs</h3>
<p>编写的库将通过 CommonJS2 规范导出，输出和使用的代码如下：</p>
<pre><code>// Webpack 输出的代码
module.exports = lib_code;

// 使用库的方法
require(&#39;library-name-in-npm&#39;).doSomething();
</code></pre><blockquote>
<p>CommonJS2 和 CommonJS 规范很相似，差别在于 CommonJS 只能用 <code>exports</code> 导出，而 CommonJS2 在 CommonJS 的基础上增加了 <code>module.exports</code> 的导出方式。</p>
</blockquote>
<p>在 <code>output.libraryTarget</code> 为 commonjs2 时，配置 <code>output.library</code> 将没有意义。    </p>
<h3 id="this">this</h3>
<p>编写的库将通过 <code>this</code> 被赋值给通过 <code>library</code> 指定的名称，输出和使用的代码如下：</p>
<p>// Webpack 输出的代码<br>this[‘LibraryName’] = lib_code;</p>
<p>// 使用库的方法<br>this.LibraryName.doSomething();</p>
<h3 id="window">window</h3>
<p>编写的库将通过 <code>window</code> 被赋值给通过 <code>library</code> 指定的名称，即把库挂载到 <code>window</code> 上，输出和使用的代码如下：</p>
<pre><code>// Webpack 输出的代码
window[&#39;LibraryName&#39;] = lib_code;

// 使用库的方法
window.LibraryName.doSomething();
</code></pre><h3 id="global">global</h3>
<p>编写的库将通过 <code>global</code> 被赋值给通过 <code>library</code> 指定的名称，即把库挂载到 <code>global</code> 上，输出和使用的代码如下：</p>
<pre><code>// Webpack 输出的代码
global[&#39;LibraryName&#39;] = lib_code;

// 使用库的方法
global.LibraryName.doSomething();
</code></pre><h3 id="libraryexport">libraryExport</h3>
<p><code>output.libraryExport</code> 配置要导出的模块中哪些子模块需要被导出。 它只有在 <code>output.libraryTarget</code> 被设置成 <code>commonjs</code> 或者 <code>commonjs2</code> 时使用才有意义。</p>
<p>假如要导出的模块源代码是：</p>
<pre><code>export const a=1;
export default b=2;
</code></pre><p>现在想让构建输出的代码只导出其中的 <code>a</code>，可以把 <code>output.libraryExport</code> 设置成 <code>a</code>，那么构建输出的代码和使用方法将变成如下：    </p>
<pre><code>// Webpack 输出的代码
module.exports = lib_code[&#39;a&#39;];

// 使用库的方法
require(&#39;library-name-in-npm&#39;)===1;
</code></pre><h2 id="module">Module</h2>
<h3 id="-loader">配置 Loader</h3>
<p><code>rules</code> 配置模块的读取和解析规则，通常用来配置 <code>Loader</code>。其类型是一个数组，数组里每一项都描述了如何去处理部分文件。 配置一项 <code>rules</code> 时大致通过以下方式：</p>
<ol>
<li><strong>条件匹配</strong>：通过 <code>test</code> 、 <code>include</code> 、 <code>exclude</code> 三个配置项来命中 Loader 要应用规则的文件。</li><li><strong>应用规则</strong>：对选中后的文件通过 <code>use</code> 配置项来应用 Loader，可以只应用一个 Loader 或者按照<strong>从后往前</strong>的顺序应用一组 Loader，同时还可以分别给 Loader 传入参数。</li><li><strong>重置顺序</strong>：一组 Loader 的执行顺序<strong>默认是从右到左执行</strong>，通过 <code>enforce</code> 选项可以让其中一个 Loader 的执行顺序放到最前或者最后。</li></ol>
<pre><code>module: {
  rules: [
    {
      // 命中 JavaScript 文件
      test: /\.js$/,
      // 用 babel-loader 转换 JavaScript 文件
      // ?cacheDirectory 表示传给 babel-loader 的参数，用于缓存 babel 编译结果加快重新编译速度
      use: [&#39;babel-loader?cacheDirectory&#39;],
      // 只命中src目录里的js文件，加快 Webpack 搜索速度
      include: path.resolve(__dirname, &#39;src&#39;)
    },
    {
      // 命中 SCSS 文件
      test: /\.scss$/,
      // 使用一组 Loader 去处理 SCSS 文件。
      // 处理顺序为从后到前，即先交给 sass-loader 处理，再把结果交给 css-loader 最后再给 style-loader。
      use: [&#39;style-loader&#39;, &#39;css-loader&#39;, &#39;sass-loader&#39;],
      // 排除 node_modules 目录下的文件
      exclude: path.resolve(__dirname, &#39;node_modules&#39;),
    },
    {
      // 对非文本文件采用 file-loader 加载
      test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
      use: [&#39;file-loader&#39;],
    },
  ]
}
</code></pre><p>在 Loader 需要传入很多参数时，你还可以通过一个 <code>Object</code> 来描述，例如在上面的 <code>babel-loader</code> 配置中有如下代码：</p>
<pre><code>use: [
  {
    loader:&#39;babel-loader&#39;,
    options:{
      cacheDirectory:true,
    },
    // enforce:&#39;post&#39; 的含义是把该 Loader 的执行顺序放到最后
    // enforce 的值还可以是 pre，代表把 Loader 的执行顺序放到最前面
    enforce:&#39;post&#39;
  },
  // 省略其它 Loader
]
</code></pre><p>上面的例子中 <code>test include exclude</code> 这三个命中文件的配置项只传入了一个字符串或正则，其实它们还都支持数组类型，使用如下：    </p>
<pre><code>{
  test:[
    /\.jsx?$/,
    /\.tsx?$/
  ],
  include:[
    path.resolve(__dirname, &#39;src&#39;),
    path.resolve(__dirname, &#39;tests&#39;),
  ],
  exclude:[
    path.resolve(__dirname, &#39;node_modules&#39;),
    path.resolve(__dirname, &#39;bower_modules&#39;),
  ]
}
</code></pre><p>数组里的每项之间是<strong>或</strong>的关系，即文件路径符合数组中的任何一个条件就会被命中。</p>
<h3 id="noparse">noParse</h3>
<p><code>noParse</code> 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析和处理，这样做的好处是能提高构建性能。 原因是一些库例如 jQuery 、ChartJS 它们庞大又没有采用模块化标准，让 Webpack 去解析这些文件耗时又没有意义。</p>
<p><code>noParse</code> 是可选配置项，类型需要是 <code>RegExp</code>、<code>[RegExp]</code>、<code>function</code> 其中一个。</p>
<p>例如想要忽略掉 jQuery 、ChartJS，可以使用如下代码：</p>
<pre><code>// 使用正则表达式
noParse: /jquery|chartjs/

// 使用函数，从 Webpack 3.0.0 开始支持
noParse: (content)=&gt; {
  // content 代表一个模块的文件路径
  // 返回 true or false
  return /jquery|chartjs/.test(content);
}
</code></pre><blockquote>
<p>注意被忽略掉的文件里不应该包含 <code>import</code> 、 <code>require</code> 、 <code>define</code> 等模块化语句，不然会导致构建出的代码中包含无法在浏览器环境下执行的模块化语句。</p>
</blockquote>
<h3 id="parser">parser</h3>
<p>因为 Webpack 是以模块化的 JavaScript 文件为入口，所以内置了对模块化 JavaScript 的解析功能，支持 <code>AMD</code>、<code>CommonJS</code>、<code>SystemJS</code>、<code>ES6</code>。 </p>
<p><code>parser</code> 属性可以更细粒度的配置哪些模块语法要解析哪些不解析，和 <code>noParse</code> 配置项的区别在于 <code>parser</code> 可以精确到语法层面， 而 <code>noParse</code> 只能控制哪些文件不被解析。 <code>parser</code> 使用如下：</p>
<pre><code>module: {
  rules: [
    {
      test: /\.js$/,
      use: [&#39;babel-loader&#39;],
      parser: {
      amd: false, // 禁用 AMD
      commonjs: false, // 禁用 CommonJS
      system: false, // 禁用 SystemJS
      harmony: false, // 禁用 ES6 import/export
      requireInclude: false, // 禁用 require.include
      requireEnsure: false, // 禁用 require.ensure
      requireContext: false, // 禁用 require.context
      browserify: false, // 禁用 browserify
      requireJs: false, // 禁用 requirejs
      }
    },
  ]
}
</code></pre><h2 id="resolve">Resolve</h2>
<p>Webpack 在启动后会从配置的入口模块出发找出所有依赖的模块，<code>Resolve</code> 配置 Webpack 如何寻找模块所对应的文件。 Webpack 内置 JavaScript 模块化语法解析功能，默认会采用模块化标准里约定好的规则去寻找，但你也可以根据自己的需要修改默认的规则。</p>
<h3 id="alias">alias</h3>
<p><code>resolve.alias</code> 配置项通过别名来把原导入路径映射成一个新的导入路径。例如使用以下配置：</p>
<pre><code>// Webpack alias 配置
resolve:{
  alias:{
    components: &#39;./src/components/&#39;
  }
}
</code></pre><p>当你通过 <code>import Button from &#39;components/button&#39;</code> 导入时，实际上被 <code>alias</code> 等价替换成了 <code>import Button from &#39;./src/components/button&#39;</code>。</p>
<p>以上 <code>alias</code> 配置的含义是把导入语句里的 <code>components</code> 关键字替换成 <code>./src/components/</code>。</p>
<p>这样做可能会命中太多的导入语句，<code>alias</code> 还支持 <code>$</code> 符号来缩小范围到只命中以关键字结尾的导入语句：</p>
<pre><code>resolve:{
  alias:{
    &#39;react$&#39;: &#39;/path/to/react.min.js&#39;
  }
}
</code></pre><p><code>react$</code> 只会命中以 <code>react</code> 结尾的导入语句，即只会把 <code>import &#39;react&#39;</code> 关键字替换成 <code>import &#39;/path/to/react.min.js&#39;</code>。</p>
<h3 id="mainfields">mainFields</h3>
<p>有一些第三方模块会针对不同环境提供几分代码。 例如分别提供采用 ES5 和 ES6 的2份代码，这2份代码的位置写在 <code>package.json</code> 文件里，如下：</p>
<pre><code>{
  &quot;jsnext:main&quot;: &quot;es/index.js&quot;,// 采用 ES6 语法的代码入口文件
  &quot;main&quot;: &quot;lib/index.js&quot; // 采用 ES5 语法的代码入口文件
}
</code></pre><p>Webpack 会根据 <code>mainFields</code> 的配置去决定优先采用哪份代码，<code>mainFields</code> 默认如下：</p>
<pre><code>mainFields: [&#39;browser&#39;, &#39;main&#39;]
</code></pre><p>Webpack 会按照数组里的顺序去 <code>package.json</code> 文件里寻找，只会使用找到的第一个。</p>
<p>假如你想优先采用 ES6 的那份代码，可以这样配置：</p>
<pre><code>mainFields: [&#39;jsnext:main&#39;, &#39;browser&#39;, &#39;main&#39;]
</code></pre><h3 id="extensions">extensions</h3>
<p>在导入语句没带文件后缀时，Webpack 会自动带上后缀后去尝试访问文件是否存在。 <code>resolve.extensions</code> 用于配置在尝试过程中用到的后缀列表，默认是：</p>
<pre><code>extensions: [&#39;.js&#39;, &#39;.json&#39;]
</code></pre><h3 id="modules">modules</h3>
<p><code>resolve.modules</code> 配置 Webpack 去哪些目录下寻找第三方模块，默认是只会去 <code>node_modules</code> 目录下寻找。 </p>
<p>有时你的项目里会有一些模块会大量被其它模块依赖和导入，由于其它模块的位置分布不定，针对不同的文件都要去计算被导入模块文件的相对路径， 这个路径有时候会很长，就像这样 <code>import &#39;../../../components/button&#39;</code> 这时你可以利用 <code>modules</code> 配置项优化，假如那些被大量导入的模块都在 <code>./src/components</code> 目录下，把 <code>modules</code> 配置成：</p>
<pre><code>modules:[&#39;./src/components&#39;,&#39;node_modules&#39;]
</code></pre><p>后，你可以简单通过 <code>import &#39;button&#39;</code> 导入。</p>
<h3 id="descriptionfiles">descriptionFiles</h3>
<p><code>resolve.descriptionFiles</code> 配置描述第三方模块的文件名称，也就是 <code>package.json</code> 文件。默认如下：</p>
<pre><code>descriptionFiles: [&#39;package.json&#39;]
</code></pre><h3 id="enforceextension">enforceExtension</h3>
<p><code>resolve.enforceExtension</code> 如果配置为 <code>true</code> 所有导入语句都必须要带文件后缀， 例如开启前 <code>import &#39;./foo&#39;</code> 能正常工作，开启后就必须写成 <code>import &#39;./foo.js&#39;</code>。</p>
<h3 id="enforcemoduleextension">enforceModuleExtension</h3>
<p><code>enforceModuleExtension</code> 和 <code>enforceExtension</code> 作用类似，但 <code>enforceModuleExtension</code> 只对 <code>node_modules</code> 下的模块生效。 </p>
<p><code>enforceModuleExtension</code> 通常搭配 <code>enforceExtension</code> 使用，在 <code>enforceExtension:true</code> 时，因为安装的第三方模块中大多数导入语句没带文件后缀， 所以这时通过配置 <code>enforceModuleExtension:false</code> 来兼容第三方模块。</p>
<h2 id="plugins">Plugins</h2>
<p>Plugin 用于扩展 Webpack 功能，各种各样的 Plugin 几乎让 Webpack 可以做任何构建相关的事情。</p>
<h3 id="-plugin">配置 Plugin</h3>
<p>Plugin 的配置很简单，<code>plugins</code> 配置项接受一个数组，数组里每一项都是一个要使用的 Plugin 的实例，Plugin 需要的参数通过<strong>构造函数</strong>传入。</p>
<pre><code>const CommonsChunkPlugin = require(&#39;webpack/lib/optimize/CommonsChunkPlugin&#39;);

module.exports = {
  plugins: [
    // 所有页面都会用到的公共代码提取到 common 代码块中
    new CommonsChunkPlugin({
      name: &#39;common&#39;,
      chunks: [&#39;a&#39;, &#39;b&#39;]
    }),
  ]
};
</code></pre><p>使用 Plugin 的难点在于掌握 Plugin 本身提供的配置项，而不是如何在 Webpack 中接入 Plugin。    </p>
<h2 id="devserver">DevServer</h2>
<p>要配置 DevServer ，除了在配置文件里通过 <code>devServer</code> 传入参数外，还可以通过命令行参数传入。 注意只有在通过 <code>DevServer</code> 去启动 Webpack 时配置文件里 <code>devServer</code> 才会生效，因为这些参数所对应的功能都是 DevServer 提供的，Webpack 本身并不认识 <code>devServer</code> 配置项。</p>
<h3 id="hot">hot</h3>
<p><code>devServer.hot</code> 配置是否启用模块热替换功能。 </p>
<p><code>DevServer</code> 默认的行为是在发现源代码被更新后会通过自动刷新整个页面来做到实时预览，开启模块热替换功能后将在不刷新整个页面的情况下通过用新模块替换老模块来做到实时预览。</p>
<h3 id="inline">inline</h3>
<p>DevServer 的实时预览功能依赖一个注入到页面里的代理客户端去接受来自 DevServer 的命令和负责刷新网页的工作。 </p>
<p><code>devServer.inline</code> 用于配置是否自动注入这个代理客户端到将运行在页面里的 Chunk 里去，默认是会自动注入。 DevServer 会根据你是否开启 <code>inline</code> 来调整它的自动刷新策略：</p>
<ul>
<li>如果开启 <code>inline</code>，DevServer 会在构建完变化后的代码时通过代理客户端控制网页刷新。</li><li>如果关闭 <code>inline</code>，DevServer 将无法直接控制要开发的网页。这时它会通过 <code>iframe</code> 的方式去运行要开发的网页，当构建完变化后的代码时通过刷新 <code>iframe</code> 来实现实时预览。 </li></ul>
<p>如果你想使用 DevServer 去自动刷新网页实现实时预览，最方便的方法是直接开启 <code>inline</code>。</p>
<h3 id="historyapifallback">historyApiFallback</h3>
<p><code>devServer.historyApiFallback</code> 用于方便的开发使用了 HTML5 History API 的单页应用。 </p>
<p>这类单页应用要求服务器在针对任何命中的路由时都返回一个对应的 HTML 文件，例如在访问 <code>http://localhost/user</code> 和 <code>http://localhost/home</code> 时都返回 <code>index.html</code> 文件， 浏览器端的 JavaScript 代码会从 URL 里解析出当前页面的状态，显示出对应的界面。</p>
<p>配置 <code>historyApiFallback</code> 最简单的做法是：</p>
<pre><code>historyApiFallback: true
</code></pre><p>这会导致任何请求都会返回 <code>index.html</code> 文件，这只能用于只有一个 HTML 文件的应用。    </p>
<p>如果你的应用由多个单页应用组成，这就需要 DevServer 根据不同的请求来返回不同的 HTML 文件，配置如下：</p>
<pre><code>historyApiFallback: {
  // 使用正则匹配命中路由
  rewrites: [
    // /user 开头的都返回 user.html
    { from: /^\/user/, to: &#39;/user.html&#39; },
    { from: /^\/game/, to: &#39;/game.html&#39; },
    // 其它的都返回 index.html
    { from: /./, to: &#39;/index.html&#39; },
  ]
}
</code></pre><h3 id="contentbase">contentBase</h3>
<p><code>devServer.contentBase</code> 配置 DevServer HTTP 服务器的文件根目录。 默认情况下为当前执行目录，通常是项目根目录，所有一般情况下你不必设置它，除非你有额外的文件需要被 DevServer 服务。 例如你想把项目根目录下的 <code>public</code> 目录设置成 DevServer 服务器的文件根目录，你可以这样配置：</p>
<pre><code>devServer:{
  contentBase: path.join(__dirname, &#39;public&#39;)
}
</code></pre><p>这里需要指出可能会让你疑惑的地方，DevServer 服务器通过 HTTP 服务暴露出的文件分为两类：</p>
<ul>
<li>暴露本地文件。</li><li>暴露 Webpack 构建出的结果，由于构建出的结果交给了 DevServer，所以你在使用了 DevServer 时在本地找不到构建出的文件。</li></ul>
<p><code>contentBase</code> 只能用来配置暴露本地文件的规则，你可以通过 <code>contentBase:false</code> 来关闭暴露本地文件。</p>
<h3 id="headers">headers</h3>
<p><code>devServer.headers</code> 配置项可以在 HTTP 响应中注入一些 HTTP 响应头，使用如下：</p>
<pre><code>devServer:{
  headers: {
    &#39;X-foo&#39;:&#39;bar&#39;
  }
}
</code></pre><h3 id="host">host</h3>
<p><code>devServer.host</code> 配置项用于配置 DevServer 服务监听的地址。 </p>
<p>例如你想要局域网中的其它设备访问你本地的服务，可以在启动 DevServer 时带上 <code>--host 0.0.0.0</code>。 <code>host</code> 的默认值是 <code>127.0.0.1</code> 即只有本地可以访问 DevServer 的 HTTP 服务。</p>
<h3 id="port">port</h3>
<p><code>devServer.port</code> 配置项用于配置 DevServer 服务监听的端口，默认使用 <code>8080</code> 端口。 如果 <code>8080</code> 端口已经被其它程序占有就使用 <code>8081</code>，如果 <code>8081</code> 还是被占用就使用 <code>8082</code>，以此类推。</p>
<h3 id="allowedhosts">allowedHosts</h3>
<p><code>devServer.allowedHosts</code> 配置一个白名单列表，只有 HTTP 请求的 HOST 在列表里才正常返回，使用如下：</p>
<pre><code>allowedHosts: [
  // 匹配单个域名
  &#39;host.com&#39;,
  &#39;sub.host.com&#39;,
  // host2.com 和所有的子域名 *.host2.com 都将匹配
  &#39;.host2.com&#39;
]
</code></pre><h3 id="disablehostcheck">disableHostCheck</h3>
<p><code>devServer.disableHostCheck</code> 配置项用于配置是否关闭用于 DNS 重绑定的 HTTP 请求的 HOST 检查。 </p>
<p>DevServer 默认只接受来自本地的请求，关闭后可以接受来自任何 HOST 的请求。 它通常用于搭配 <code>--host 0.0.0.0</code> 使用，因为你想要其它设备访问你本地的服务，但访问时是直接通过 IP 地址访问而不是 HOST 访问，所以需要关闭 HOST 检查。</p>
<h3 id="https">https</h3>
<p>DevServer 默认使用 HTTP 协议服务，它也能通过 HTTPS 协议服务。 有些情况下你必须使用 HTTPS，例如 HTTP2 和 Service Worker 就必须运行在 HTTPS 之上。 要切换成 HTTPS 服务，最简单的方式是：</p>
<pre><code>devServer:{
  https: true
}
</code></pre><p>DevServer 会自动的为你生成一份 HTTPS 证书。</p>
<p>如果你想用自己的证书可以这样配置：</p>
<pre><code>devServer:{
  https: {
    key: fs.readFileSync(&#39;path/to/server.key&#39;),
    cert: fs.readFileSync(&#39;path/to/server.crt&#39;),
    ca: fs.readFileSync(&#39;path/to/ca.pem&#39;)
  }
}
</code></pre><h3 id="clientloglevel">clientLogLevel</h3>
<p><code>devServer.clientLogLevel</code> 配置在客户端的日志等级，这会影响到你在浏览器开发者工具控制台里看到的日志内容。 </p>
<p><code>clientLogLevel</code> 是<strong>枚举类型</strong>，可取如下之一的值 <code>none | error | warning | info</code>。 默认为 <code>info</code> 级别，即输出所有类型的日志，设置成 <code>none</code> 可以不输出任何日志。</p>
<h3 id="compress">compress</h3>
<p><code>devServer.compress</code> 配置是否启用 gzip 压缩。<code>boolean</code> 为类型，默认为 <code>false</code>。</p>
<h3 id="open">open</h3>
<p><code>devServer.open</code> 用于在 DevServer 启动且第一次构建完时自动用你系统上默认的浏览器去打开要开发的网页。 同时还提供 <code>devServer.openPage</code> 配置项用于打开指定 URL 的网页。</p>
<h2 id="-">其它配置项</h2>
<h3 id="target">Target</h3>
<p><code>target</code> 配置项可以让 Webpack 构建出针对不同运行环境的代码。 target 可以是以下之一：</p>
<table>
<thead>
<tr>
<th>target值</th>
<th>描述           </th>
</tr>
</thead>
<tbody>
<tr>
<td>web</td>
<td>针对浏览器 <strong>(默认)</strong>，所有代码都集中在一个文件里</td>
</tr>
<tr>
<td>node</td>
<td>针对 Node.js，使用 <code>require</code> 语句加载 Chunk 代码    </td>
</tr>
<tr>
<td>async-node</td>
<td>针对 Node.js，异步加载 Chunk 代码</td>
</tr>
<tr>
<td>webworker</td>
<td>针对 WebWorker</td>
</tr>
<tr>
<td>electron-main</td>
<td>针对 <a href="https://electronjs.org/">Electron</a> 主线程</td>
</tr>
<tr>
<td>electron-renderer</td>
<td>针对 Electron 渲染线程</td>
</tr>
</tbody>
</table>
<p>例如当你设置 <code>target:&#39;node&#39;</code> 时，源代码中导入 Node.js 原生模块的语句 <code>require(&#39;fs&#39;)</code> 将会被保留，<code>fs</code> 模块的内容不会打包进 Chunk 里。</p>
<h3 id="devtool">Devtool</h3>
<p><code>devtool</code> 配置 Webpack 如何生成 Source Map，默认值是 <code>false</code> 即不生成 Source Map，想为构建出的代码生成 Source Map 以方便调试，可以这样配置：</p>
<pre><code>module.export = {
  devtool: &#39;source-map&#39;
}
</code></pre><h3 id="watch-watchoptions">Watch 和 WatchOptions</h3>
<p>前面介绍过 Webpack 的监听模式，它支持监听文件更新，在文件发生变化时重新编译。在使用 Webpack 时监听模式默认是关闭的，想打开需要如下配置：</p>
<pre><code>module.export = {
  watch: true
}
</code></pre><p>在使用 DevServer 时，监听模式默认是开启的。</p>
<p>除此之外，Webpack 还提供了 <code>watchOptions</code> 配置项去更灵活的控制监听模式，使用如下：</p>
<pre><code>module.export = {
  // 只有在开启监听模式时，watchOptions 才有意义
  // 默认为 false，也就是不开启
  watch: true,
  // 监听模式运行时的参数
  // 在开启监听模式时，才有意义
  watchOptions: {
    // 不监听的文件或文件夹，支持正则匹配
    // 默认为空
    ignored: /node_modules/,
    // 监听到变化发生后会等300ms再去执行动作，防止文件更新太快导致重新编译频率太高
    // 默认为 300ms  
    aggregateTimeout: 300,
    // 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
    // 默认每1000豪秒去问1次
    poll: 1000
  }
}
</code></pre><h3 id="externals">Externals</h3>
<p>Externals 用来告诉 Webpack 要构建的代码中使用了哪些不用被打包的模块，也就是说这些模版是外部环境提供的，Webpack 在打包时可以忽略它们。</p>
<p>有些 JavaScript 运行环境可能内置了一些全局变量或者模块，例如在你的 HTML HEAD 标签里通过以下代码：</p>
<pre><code>&lt;script src=&quot;path/to/jquery.js&quot;&gt;&lt;/script&gt;
</code></pre><p>引入 jQuery 后，全局变量 <code>jQuery</code> 就会被注入到网页的 JavaScript 运行环境里。</p>
<p>如果想在使用模块化的源代码里导入和使用 jQuery，可能需要这样：</p>
<pre><code>import $ from &#39;jquery&#39;;
$(&#39;.my-element&#39;);
</code></pre><p>构建后你会发现输出的 Chunk 里包含的 jQuery 库的内容，这导致 jQuery 库出现了2次，浪费加载流量，最好是 Chunk 里不会包含 jQuery 库的内容。</p>
<p>Externals 配置项就是为了解决这个问题。</p>
<p>通过 <code>externals</code> 可以告诉 Webpack JavaScript 运行环境已经内置了那些全局变量，针对这些全局变量不用打包进代码中而是直接使用全局变量。 要解决以上问题，可以这样配置 <code>externals</code>：</p>
<pre><code>module.export = {
  externals: {
    // 把导入语句里的 jquery 替换成运行环境里的全局变量 jQuery
    jquery: &#39;jQuery&#39;
  }
}
</code></pre><h3 id="resolveloader">ResolveLoader</h3>
<p>ResolveLoader 用来告诉 Webpack 如何去寻找 Loader，因为在使用 Loader 时是通过其包名称去引用的， Webpack 需要根据配置的 Loader 包名去找到 Loader 的实际代码，以调用 Loader 去处理源文件。</p>
<p>ResolveLoader 的默认配置如下：</p>
<pre><code>module.exports = {
  resolveLoader:{
    // 去哪个目录下寻找 Loader
    modules: [&#39;node_modules&#39;],
    // 入口文件的后缀
    extensions: [&#39;.js&#39;, &#39;.json&#39;],
    // 指明入口文件位置的字段
    mainFields: [&#39;loader&#39;, &#39;main&#39;]
  }
}
</code></pre><p>该配置项常用于加载本地的 Loader。</p>
<h2 id="-">整体配置结构</h2>
<p>之前的章节分别讲述了每个配置项的具体含义，但没有描述它们所处的位置和数据结构，下面通过一份代码来描述清楚：</p>
<p><p data-height="565" data-theme-id="0" data-slug-hash="RyORja" data-default-tab="js,result" data-user="whjin" data-embed-version="2" data-pen-title="webpack整体配置" class="codepen">See the Pen <a href="https://codepen.io/whjin/pen/RyORja/">webpack整体配置</a> by whjin (<a href="https://codepen.io/whjin">@whjin</a>) on <a href="https://codepen.io">CodePen</a>.</p></p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

<h2 id="-">多种配置类型</h2>
<p>除了通过导出一个 Object 来描述 Webpack 所需的配置外，还有其它更灵活的方式，以简化不同场景的配置。</p>
<h3 id="-function">导出一个 Function</h3>
<p>在大多数时候你需要从同一份源代码中构建出多份代码，例如一份用于开发时，一份用于发布到线上。</p>
<p>如果采用导出一个 Object 来描述 Webpack 所需的配置的方法，需要写两个文件。 一个用于开发环境，一个用于线上环境。再在启动时通过 <code>webpack --config webpack.config.js</code> 指定使用哪个配置文件。</p>
<p>采用导出一个 Function 的方式，能通过 JavaScript 灵活的控制配置，做到只写一个配置文件就能完成以上要求。</p>
<p>导出一个 Function 的使用方式如下：</p>
<p><p data-height="365" data-theme-id="0" data-slug-hash="zjXBmQ" data-default-tab="js,result" data-user="whjin" data-embed-version="2" data-pen-title="webpack-导出一个Function" class="codepen">See the Pen <a href="https://codepen.io/whjin/pen/zjXBmQ/">webpack-导出一个Function</a> by whjin (<a href="https://codepen.io/whjin">@whjin</a>) on <a href="https://codepen.io">CodePen</a>.</p></p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

<p>在运行 Webpack 时，会给这个函数传入2个参数，分别是：</p>
<ol>
<li><code>env</code>：当前运行时的 Webpack 专属环境变量，<code>env</code> 是一个 Object。读取时直接访问 Object 的属性，设置它需要在启动 Webpack 时带上参数。例如启动命令是 <code>webpack --env.production --env.bao=foo</code> 时，则 <code>env</code> 的值是 <code>{&quot;production&quot;:&quot;true&quot;,&quot;bao&quot;:&quot;foo&quot;}</code>。</li><li><code>argv</code>：代表在启动 Webpack 时所有通过命令行传入的参数，例如 <code>--config、--env、--devtool</code>，可以通过 <code>webpack -h</code> 列出所有 Webpack 支持的命令行参数。</li></ol>
<p>就以上配置文件而言，在开发时执行命令 webpack 构建出方便调试的代码，在需要构建出发布到线上的代码时执行 <code>webpack --env.production</code> 构建出压缩的代码。</p>
<h3 id="-promise-">导出一个返回 Promise 的函数</h3>
<p>在有些情况下你不能以同步的方式返回一个描述配置的 Object，Webpack 还支持导出一个返回 Promise 的函数，使用如下：</p>
<pre><code>module.exports = function(env = {}, argv) {
  return new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
      resolve({
        // ...
      })
    }, 5000)
  })
}
</code></pre><h3 id="-">导出多份配置</h3>
<p>除了只导出一份配置外，Webpack 还支持导出一个数组，数组中可以包含每份配置，并且每份配置都会执行一遍构建。</p>
<p>使用如下：</p>
<pre><code>module.exports = [
  // 采用 Object 描述的一份配置
  {
    // ...
  },
  // 采用函数描述的一份配置
  function() {
    return {
      // ...
    }
  },
  // 采用异步函数描述的一份配置
  function() {
    return Promise();
  }
]
</code></pre><p>以上配置会导致 Webpack 针对这三份配置执行三次不同的构建。</p>
<p>这特别适合于用 Webpack 构建一个要上传到 Npm 仓库的库，因为库中可能需要包含多种模块化格式的代码，例如 CommonJS、UMD。</p>
<h2 id="-">配置总结</h2>
<p>从前面的配置看来选项很多，Webpack 内置了很多功能。 </p>
<p>你不必都记住它们，只需要大概明白 Webpack 原理和核心概念去判断选项大致属于哪个大模块下，再去查详细的使用文档。</p>
<p>通常你可用如下经验去判断如何配置 Webpack：</p>
<ul>
<li>想让<strong>源文件</strong>加入到构建流程中去被 Webpack 控制，配置 <code>entry</code>。</li><li>想自定义<strong>输出文件的位置和名称</strong>，配置 <code>output</code>。</li><li>想自定义<strong>寻找依赖模块时的策略</strong>，配置 <code>resolve</code>。</li><li>想自定义<strong>解析和转换文件的策略</strong>，配置 <code>module</code>，通常是配置 <code>module.rules</code> 里的 Loader。</li><li>其它的大部分需求可能要通过 Plugin 去实现，配置 <code>plugin</code>。</li></ul>

</body>
</html>
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
